package com.vip.jwt.common.utils.xcx.wx;

import cn.hutool.http.HttpRequest;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.vip.jwt.common.exception.MyException;
import com.vip.jwt.common.utils.JsonUtil;
import com.vip.jwt.common.utils.RedisUtil;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.Base64Utils;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.AlgorithmParameterSpec;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 微信授权工具类
 */
public class WXAuthUtil {

    static String key = "wx_xcx_access_token";

    @Getter
    private static String APP_ID;

    @Getter
    private static String APP_SECRET;

    @Value("${wx-xcx-appid}")
    public void setAppId(String appId) {
        APP_ID = appId;
    }

    @Value("${wx-xcx-appsecret}")
    public void setAppSecret(String appSecret) {
        APP_SECRET = appSecret;
    }

    /**
     * 获取SessionKey和Openid（如果sessionKey没有过期，那么再次使用code去获取sessionKey时可能会报错）
     *
     * @return
     */
    public static WXSo getSessionKeyAndOpenid(String wxCode) {
        String url = "https://api.weixin.qq.com/sns/jscode2session?" + "appid=" + APP_ID + "&secret=" + APP_SECRET + "&js_code=" + wxCode + "&grant_type=authorization_code";
        String jsonStr = HttpRequest.get(url).header("Content-type", "application/json").execute().body();
        JSONObject jsonObject = JSONUtil.parseObj(jsonStr);
        WXSo wxSo = new WXSo();
        wxSo.setSessionKey(jsonObject.getStr("session_key"));
        wxSo.setOpenid(jsonObject.getStr("openid"));
        return wxSo;
    }

    /**
     * 获取手机号
     *
     * @param sessionKey    使用code获取的sessionKey（sessionKey需要存在数据库，如果没过期，则使用数据库里面的，如果过期了，则重新获取
     *                      所以这里需要前台先检查是否过期，使用wx.checkSession检查是否过期，使用wx.login获取code）
     * @param encryptedData 加密的数据
     * @param iv            解密串
     * @return
     */
    public static String getPhone(String sessionKey, String encryptedData, String iv) {
        //解密
        byte[] encrypData = Base64Utils.decodeFromString(encryptedData);
        byte[] ivData = Base64Utils.decodeFromString(iv);
        byte[] sessionKeyData = Base64Utils.decodeFromString(sessionKey);
        AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivData);
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKeySpec keySpec = new SecretKeySpec(sessionKeyData, "AES");
            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); //设置为解密模式
            String jsonStr = new String(cipher.doFinal(encrypData), StandardCharsets.UTF_8);
            JSONObject jsonObject = JSONUtil.parseObj(jsonStr);
            return jsonObject.get("phoneNumber").toString();
        } catch (NoSuchAlgorithmException
                | BadPaddingException
                | InvalidKeyException
                | InvalidAlgorithmParameterException
                | NoSuchPaddingException
                | IllegalBlockSizeException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取令牌
     *
     * @return
     */
    private static String accessToken() {
        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + APP_ID + "&secret=" + APP_SECRET;
        return HttpRequest.get(url).header("Content-type", "application/json").execute().body();
    }

    /**
     * 将AccessToken存入Redis
     *
     * @return
     */
    public static String getAccessToken() {

        // 首先验证Redis有没有accessToken
        Boolean bo = RedisUtil.hasKey(key);
        String accessTokenStr;
        if (bo) {
            accessTokenStr = RedisUtil.get(key);
        } else {
            String jsonStr = accessToken();
            JSONObject jsonObject = JSONUtil.parseObj(jsonStr);
            if (null != jsonObject.get("errcode")) {
                throw new MyException(jsonObject.get("errmsg").toString());
            }
            accessTokenStr = jsonObject.get("access_token").toString();
            RedisUtil.set(key, accessTokenStr);
            int expires_in = Integer.parseInt(jsonObject.get("expires_in").toString());
            RedisUtil.expire(key, expires_in, TimeUnit.SECONDS);
        }
        return accessTokenStr;
    }

    /**
     * 发送订阅消息
     *
     * @param accessToken 令牌
     * @param openId      用户的唯一标识
     * @param dataMap     参数，示例如下：
     *                    Map<String,Object> dataMap = new HashMap<>(16);
     *                    Map<String,String> thingMap = new HashMap(16);
     *                    thingMap.put("value","张三");
     *                    dataMap.put("thing2",thingMap);
     *                    Map<String,String> phraseMap = new HashMap(16);
     *                    phraseMap.put("value","报名成功");
     *                    dataMap.put("phrase3",phraseMap);
     */
    public static String sendSubscribeMessage(String accessToken, String templateId, String openId, Map<String, Object> dataMap) {
        String url = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" + accessToken;
        Map<String, Object> params = new HashMap<>(16);
        params.put("touser", openId);
        params.put("template_id", templateId);
        params.put("page", "index");
        params.put("miniprogram_state", "trial");
        params.put("lang", "zh_CN");
        params.put("data", dataMap);
        String dataJson = new JSONObject(params).toString();
        return HttpRequest.post(url).body(dataJson).execute().body();
    }

    /**
     * 发送模板消息
     *
     * @param accessToken 令牌
     * @param openId      用户的唯一标识
     * @param dataMap     参数，示例如下：
     *                    Map<String,Object> dataMap = new HashMap<>(16);
     *                    Map<String,String> thingMap = new HashMap(16);
     *                    thingMap.put("value","张三");
     *                    dataMap.put("thing2",thingMap);
     *                    Map<String,String> phraseMap = new HashMap(16);
     *                    phraseMap.put("value","报名成功");
     *                    dataMap.put("phrase3",phraseMap);
     */
    public static String sendTemplateMessage(String accessToken,String toUrl, String templateId, String openId, Map<String, Object> dataMap) {
        String url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" + accessToken;
        Map<String, Object> params = new HashMap<>(16);
        params.put("touser", openId);
        params.put("url",toUrl);
        params.put("template_id", templateId);
//        params.put("page","index");
//        params.put("miniprogram_state","trial");
        params.put("lang", "zh_CN");
        params.put("data", dataMap);
        String dataJson = new JSONObject(params).toString();
        return HttpRequest.post(url).body(dataJson).execute().body();
    }

    /**
     * 获取微信用户openid
     */
    public static JSONArray getWxUserOpenId() {
        String token = getAccessToken();
//        https://api.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&next_openid=NEXT_OPENID
        String url = "https://api.weixin.qq.com/cgi-bin/user/get?access_token=" + token;
        String body = HttpRequest.get(url).execute().body();
        JSONObject jsonObject = JSONUtil.parseObj(body);
        if ("40001".equals(jsonObject.get("errcode"))){
            if ((jsonObject.get("errmsg").toString().contains("access_token is invalid"))&&RedisUtil.hasKey(key)) {
                RedisUtil.delete(key);
                return getWxUserOpenId();
            }else{
                throw new MyException(jsonObject.get("errmsg").toString());
            }
        }

        return jsonObject.getJSONObject("data").getJSONArray("openid");
    }


    /**
     * 获取微信用户基本信息
     */
    public static WXUserInfoOA getWxUserInfo(String OpenId) {
        String token = getAccessToken();
        String url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=" + token + "&openid=" + OpenId + "&lang=zh_CN";
        String body = HttpRequest.get(url).execute().body();
        JSONObject jsonObject = JSONUtil.parseObj(body);
        if ("40001".equals(jsonObject.get("errcode"))){
            if ((jsonObject.get("errmsg").toString().contains("access_token is invalid"))&&RedisUtil.hasKey(key)) {
                RedisUtil.delete(key);
                return getWxUserInfo(OpenId);
            }else{
                throw new MyException(jsonObject.get("errmsg").toString());
            }
        }
        return jsonObject.toBean(WXUserInfoOA.class);
    }

    /**
     * 获取用户信息列表用户信息
     */
    public static List<WXUserInfoOA> syncUserInfo() {
        JSONArray wxUserOpenId=new JSONArray();
        String token = getAccessToken();
        String url = "https://api.weixin.qq.com/cgi-bin/user/get?access_token=" + token;
        String body = HttpRequest.get(url).execute().body();
        JSONObject jsonObject = JSONUtil.parseObj(body);
        if ("40001".equals(jsonObject.get("errcode"))){
            if ((jsonObject.get("errmsg").toString().contains("access_token is invalid"))&&RedisUtil.hasKey(key)) {
                RedisUtil.delete(key);
                wxUserOpenId = getWxUserOpenId();
            }else{
                throw new MyException(jsonObject.get("errmsg").toString());
            }
        }else{
            wxUserOpenId=jsonObject.getJSONObject("data").getJSONArray("openid");
        }
        List<WXUserInfoOA> WXUserInfoOAList= new ArrayList<>();
        for (Object o : wxUserOpenId) {
            WXUserInfoOA wxUserInfo = getWxUserInfo(o.toString());
            WXUserInfoOAList.add(wxUserInfo);
        }
        return WXUserInfoOAList;
    }

}